package generator.workflow.sfj.gridgain.j2xml_impl;

import generator.datastructure.ForkPoint;
import generator.datastructure.MethodDeclaration;
import generator.datastructure.VarDeclaration;
import generator.datastructure.WritebleClass;
import generator.datastructure.j2xml_impl.DocumentWrapper;
import generator.datastructure.j2xml_impl.ForkPointGridified;
import generator.datastructure.j2xml_impl.ForkPointJ2XML;
import generator.datastructure.j2xml_impl.VarDecJ2XML;
import generator.util.DocTransformer;
import generator.util.J2XMLManipulator;
import generator.util.input_interpreter.ParallelizerInterpreter;
import generator.workflow.sfj.gridgain.GridGainPeer;
import generator.workflow.sfj.gridgain.GridJob;
import generator.workflow.sfj.gridgain.GridResult;

import java.io.File;
import java.util.ArrayList;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/** 
 * Las operaciones que implementa tienen por objetivo manipular el código fuente
 * para hacer posible el uso del middleware GridGain y se llevan a cabo empleando el modelo de
 * representación de clase java impuesto por la biblioteca JavaToXml.*/
public class GridGainPeer_J2XML extends GridGainPeer{
	
	public static final String sequentialMethodSuffix = "_sequential";

	/**
	 * Documento que representa la clase clase java convencional codificada por el usuario.
	 * El mismo constituye el producto base sobre el cual este builder trabaja para lograr
	 * la gridificación de los métodos indicados por el usuario. 	  
	 */
	protected org.w3c.dom.Document gridifiableClass = null;

	/**Documento del que estrae ciertas estructuras pre armadas que utilizará durante la generación
	 * de la clase peer*/
	protected org.w3c.dom.Document preBuildStructures;
	
	/**Objeto utilizado para evaluar expresiones xpath sobre archivos xml*/
	private XPath xpath;
	
	/**Objeto que se utilizará para realizar operaciones genéricas sobre clases java
	 * representadas como documentos que responden al formato impuesto por la biblioteca
	 * JavaToXml*/
	private J2XMLManipulator cm = null;
	
	private String PEERCLASS_SUFFIX = "Peer";
		
	
	/**Constructor de la clase en la que se inicializan las variables que la misma utiliza para
	 * la generación de la clase peer. 
	 * @param inputReader
	 * 			provee información la ruta de la clase que contiene los métodos a gridificar*/
	public GridGainPeer_J2XML(){
		
	}
	
	/**{@inheritDoc}*/
	@Override	
	public boolean loadSequentialClass(ParallelizerInterpreter inputReader) {
				 
		try {
			if (cm == null)
				cm = new J2XMLManipulator();		
			xpath = XPathFactory.newInstance().newXPath();
			preBuildStructures = DocTransformer.xmlToDom(new File(cm.getPreBuildStructuresPath()));		
			this.gridifiableClass = J2XMLManipulator.classToDOM(inputReader.getClassToParallelize());
			
			String className = (String)xpath.evaluate("//"+cm.CLASS+"[1]/@name", gridifiableClass, XPathConstants.STRING);
			cm.setClassName(className+PEERCLASS_SUFFIX , gridifiableClass);
			this.setSerializableInterface(className+PEERCLASS_SUFFIX,gridifiableClass);
			return true;
		} catch (Exception e) {
			System.err.println("Falló la carga de la clase a paralelizar");
			return false;
		}
		
	}
	
	/**@return retorna el objeto que se utilizará para realizar operaciones genéricas sobre
	 * clases java representadas como documentos que responden al formato impuesto por la biblioteca
	 * JavaToXml*/
	public J2XMLManipulator getCm() {
		return cm;
	}

	/**@param cm
	 * 			Objeto que se utilizará para realizar operaciones genéricas sobre clases java
	 * 			representadas como documentos que responden al formato impuesto por la biblioteca
	 * 			JavaToXml*/
	public void setCm(J2XMLManipulator cm) {
		this.cm = cm;
	}
	
	/**Hace que la clase Peer implemente la interface {@link java.io.Serializable}. Antes de
	 * realizar la correspondiente modificación chequea que la clase original ya no implemente
	 * dicha interfaz, si es así, el cambio no se lleva a cabo.
	 * @param className
	 * 			nombre de la clase que recibirá la modificación en su declaración.
	 * @param gridifiableClass
	 * 			documento que contienen la clase a modificar.*/
	private void setSerializableInterface(String className, Document gridifiableClass) {
		try {
			if (!cm.alreadyImplementInterface("Serializable", className, gridifiableClass)
				&& !cm.alreadyImplementInterface("java.io.Serializable", className, gridifiableClass)){ 
				cm.setImplementInterface("java.io.Serializable",className,gridifiableClass);
			}
		} catch (XPathExpressionException e) {			
			e.printStackTrace();
		}		
	}


	/**{@inheritDoc}
	 * Limitaciones: en el cuerpo del método a gridificar no puede haber una invocación a otro método
	 * que se llame igual al que se desea gridificar pero que tenga distintos parámetros, es decir,
	 * se asume que si en el cuerpo del método a gridificar se encuentra una invocación a un método
	 * con el mismo nombre en realidad se trata de un llamado recursivo.*/
	@Override
	public java.util.List<ForkPoint> getForks(MethodDeclaration md) {	
		
		Element method = cm.getMethodImpl(md, gridifiableClass);
		String mName = md.getMethodName();
		
		//la siguiente expresion xpath pretende recuperar los llamados recursivos contenidos en cuerpo del método a gridificar
		//Dichos llamados pueden venir bajo dos estructuras de nodos diferentes: como una declaración de variable acompañada de
		//de su asignación que es el resultado del llamado recursivo, o como una simple asignación del valor del llamado recursivo.  
		String forksQuery = "self::*//"+cm.LOCALVARIABLE+"["+cm.METHODCALL+"[@message='"+mName+"']/"+cm.TARGET+"/"+cm.THIS+"] | //"+cm.ASIGMENT+"["+cm.ASSIGLEFTVALUE+"/"+cm.VARREF+" and "+cm.METHODCALL+"[@message='"+mName+"']/"+cm.TARGET+"/"+cm.THIS+"]";
		ArrayList<ForkPoint> forkPoints = new ArrayList<ForkPoint>(); 
		try {
			NodeList forks = (NodeList)xpath.evaluate(forksQuery, method, XPathConstants.NODESET);
			
			for (int i = 0; i < forks.getLength(); i++){				
				forkPoints.add(new ForkPointJ2XML((Element)forks.item(i),md));
			} 
		} catch (XPathExpressionException e) {
			System.err.println("Fallo obtención de forks");
		}
		return forkPoints;		
	}

	
	/**{@inheritDoc}*/
	@Override
	public WritebleClass getPeerClass() {
		return new DocumentWrapper(gridifiableClass);
	}
	

	/**{@inheritDoc}*/
	public void setImports() {
		try {
			NodeList importsList = (NodeList)xpath.evaluate("//structure[@id='gridgainImports']/*", preBuildStructures, XPathConstants.NODESET);
			for(int i = 0; i < importsList.getLength(); i++){
				cm.addImport(importsList.item(i), gridifiableClass);
			}
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}		
	}
	
	
	/**{@inheritDoc}
	 * En este método tambien se realiza la inserción del control de nivel de recursión, la inserción de los métodos
	 * de inicio y detención del grid y el método de indirección que realiza el llamado al método que se gridificara.
	 * Las acciones anteriores se aplican en este método, ya que se garantiza una unica ejecución del mismo.
	 * */
	public void createLocalExecutionMethod(ForkPoint fork) {
		String localExecutionMethod = fork.getMethodName()+GridGainPeer_J2XML.sequentialMethodSuffix;
		boolean hasLocalMethod;
		try {
			hasLocalMethod = (Boolean)xpath.evaluate("//"+cm.METHOD+"/@name='"+localExecutionMethod+"'", gridifiableClass, XPathConstants.BOOLEAN);
		
			if(!hasLocalMethod){//significa que tengo que agregar el método de ejecución local (mecanismo para evitar seguir creando trabajos)
				Element originalMethod = cm.getMethodImpl(fork.getMethodOwner(), gridifiableClass);
				Element methodCloned = (Element)originalMethod.cloneNode(true);
				methodCloned.setAttribute("name", localExecutionMethod);
				NodeList internalCalls = (NodeList)xpath.evaluate("self::*//"+cm.METHODCALL+"[@message='"+fork.getMethodName()+"']", methodCloned, XPathConstants.NODESET);
				for (int i = 0; i < internalCalls.getLength(); i++){
					((Element)internalCalls.item(i)).setAttribute("message", localExecutionMethod);
				}
				originalMethod.getParentNode().insertBefore(methodCloned, originalMethod);
				//addIndirectCall(fork);
				addRecLevelControl(fork);
				addStartGridMethod();
				addStopGridMethod();
			}		
		} catch (XPathExpressionException e) {
			System.err.println("Falló la creación del método de ejecución local");
		}
		
	}
	
	/**
	 * Método encargado de insertar, en la clase Peer, el método que detiene la ejecución del grid
	 * @throws XPathExpressionException
	 */
	private void addStopGridMethod() throws XPathExpressionException {
		Element method = (Element)xpath.evaluate("//structure[@id='stopGridMethod']/"+cm.METHOD, preBuildStructures, XPathConstants.NODE);
		method = (Element)gridifiableClass.importNode(method, true);
		// Agrego el método a la clase
		Node classTag = gridifiableClass.getElementsByTagName(cm.CLASS).item(0);
		classTag.appendChild(method);	
	}

	/**
	 * Método encargado de insertar, en la clase Peer, el método que inicia la ejecución del grid
	 * @throws XPathExpressionException
	 */
	private void addStartGridMethod() throws XPathExpressionException {
		Element method = (Element)xpath.evaluate("//structure[@id='startGridMethod']/"+cm.METHOD, preBuildStructures, XPathConstants.NODE);
		method = (Element)gridifiableClass.importNode(method, true);
		// Agrego el método a la clase
		Node classTag = gridifiableClass.getElementsByTagName(cm.CLASS).item(0);
		classTag.appendChild(method);
		
	}

	/**{@inheritDoc}*/
	public void declareFuture(ForkPoint fork) {
		try{
			//levanto una declaracion de future generica
			Element futureDec = (Element)xpath.evaluate("//structure[@id='futureParametrizedDec']/"+cm.LOCALVARIABLE, preBuildStructures, XPathConstants.NODE);			
			String prefixName =  futureDec.getAttribute("name");
			Element asign = ((ForkPointJ2XML)fork).getForkElement();
			
			//chequeo que ya no exista la declaración del future									
			Element declare = cm.getDeclaration(asign);
			if (declare != null){//significa que la declaracion de la variable grid no ha sido reemplazada aún por la declaración de su future asociado
				boolean existDeclare = (Boolean)xpath.evaluate("self::*//"+cm.LOCALVARIABLE+"[@name='"+prefixName+fork.getGridVariableName()+"']", declare.getParentNode(), XPathConstants.BOOLEAN);
				if (fork.hasDeclaration()){//significa que la declaración del future será en la misma línea que el fork				
					Element type = (Element)xpath.evaluate("self::*//"+cm.TYPE+"[1]", futureDec, XPathConstants.NODE);
					type = (Element)asign.getOwnerDocument().importNode(type, true);
					asign.replaceChild(type,asign.getChildNodes().item(0));
					futureDec = asign;
				}
				else{
					Element varRef = (Element)xpath.evaluate("self::*//"+cm.ASSIGLEFTVALUE+"/"+cm.VARREF, asign, XPathConstants.NODE);
					varRef.setAttribute("name", prefixName+fork.getGridVariableName());					
					if (!existDeclare){//si no existe la declaracion del future, lo declaro
						futureDec = (Element)asign.getOwnerDocument().importNode(futureDec, true);					
						declare.getParentNode().insertBefore(futureDec, declare);
					}
					
				}			
				if (!existDeclare){//si no existe la declaracion del future, significa que lo declaré en este paso, con lo cual debo establecerle el tipo y nombre correcto
					Element innerType = (Element)xpath.evaluate("self::*//"+cm.PARAMETRIZED_TYPE+"[1]/"+cm.TYPE, futureDec, XPathConstants.NODE);
					innerType.setAttribute("name", GridResult.getGRName(fork.getMethodOwner()));
					futureDec.setAttribute("name", prefixName+fork.getGridVariableName());
				}
			}			
		} catch (XPathExpressionException e) {
			System.err.println("Falló la declaración de variable Future");
		}
	}
	
	
	/** 
	 * {@inheritDoc}
	 * Ejemplo de transformación seguido por este método:<P>
	 * <PRE>
	 * 	<b>this.fib(a);</b><BR>
	 * será transformado a:<BR>
	 * 	<b>this.getGrid().execute(new GridExecutorCallableTask(),new JobClass(this,a));</b><br>
	 * </PRE>
	 * Donde el nombre de JobClass será consultado a la clase GridJob y la instancia de grid será 
	 * también agregada por este método junto a su inicialización.
	 * @param fork
	 * 			contiene el llamado recursivo que pretende convertirse en un trabajo para ser ejecutado
	 * 			en el grid.
	 * @return la sentencia fork gridificada.
	 * */	
	public ForkPoint submitJobOnGrid(ForkPoint fork) {
		try {			
			Element submitJobSentence = (Element)xpath.evaluate("//structure[@id='submitJobOnGrid']/*", preBuildStructures, XPathConstants.NODE);		
			Element forkSentence = ((ForkPointJ2XML)fork).getForkElement();		
			submitJobSentence = (Element)forkSentence.getOwnerDocument().importNode(submitJobSentence, true);
			//conciliación de parámetros entre el fork y el job
			Element constructorJobArgs = ((Element)xpath.evaluate("self::*//"+cm.NEW+"["+cm.TYPE+"[@name='JobClass']]/"+cm.ARGUMENTS, submitJobSentence, XPathConstants.NODE));		
			Element forkArgs = ((Element)xpath.evaluate("self::*//"+cm.METHODCALL+"[@message='"+fork.getMethodName()+"']/"+cm.ARGUMENTS, forkSentence, XPathConstants.NODE));			
			cm.cloneChilds(forkArgs,constructorJobArgs,false);
			//seteo del nombre de la clase job
			Element jobClass =  (Element)xpath.evaluate("self::*//"+cm.NEW+"/"+cm.TYPE+"[@name='JobClass']", submitJobSentence, XPathConstants.NODE);
			jobClass.setAttribute("name", GridJob.getGJName(fork.getMethodOwner()));
			//reemplazo de sentencia fork convencional por sentencia fork gridificada
			Element asignRightValue = (Element)xpath.evaluate("self::*//"+cm.METHODCALL+"[@message='"+fork.getMethodName()+"']", forkSentence, XPathConstants.NODE);
			forkSentence.replaceChild(submitJobSentence, asignRightValue);
			return new ForkPointGridified(forkSentence,fork.getMethodOwner());
		} catch (XPathExpressionException e) {
			System.err.println("Falló la conversión de sentencia fork convencional en sentencia fork gridificada");
			return null;
		}
		
	}

	

	/**{@inheritDoc}*/
	@Override
	public VarDeclaration isolate(ForkPoint fork) {
		Element forkPoint = ((ForkPointJ2XML)fork).getForkElement();
		if(fork.hasDeclaration()){
			try {
				Element newDeclare = (Element)xpath.evaluate("//structure[@id='localVarDec']/*",preBuildStructures,XPathConstants.NODE);
				newDeclare = (Element) forkPoint.getOwnerDocument().importNode(newDeclare, true);
				String varName = fork.getGridVariableName();
				String varType = fork.getMethodOwner().getReturnType();
				newDeclare.setAttribute("name", varName);
				Element type = (Element)xpath.evaluate("self::*/"+cm.TYPE,newDeclare,XPathConstants.NODE);
				type.setAttribute("name", varType);
				forkPoint.getParentNode().insertBefore(newDeclare, forkPoint);
				return new VarDecJ2XML(varName,varType,newDeclare);
			} catch (XPathExpressionException e) {
				e.printStackTrace();
			}
		}
		Element declare = cm.getDeclaration(forkPoint);
		String varName = declare.getAttribute("name");
		String varType = ((Element)declare.getElementsByTagName(cm.TYPE).item(0)).getAttribute("name");
		return new VarDecJ2XML(varName,varType,declare);
	}
	
	/**{@inheritDoc}*/
	@Override
	protected void replaceGridVarUses(VarDeclaration gridVariableDec,ForkPoint gridifiedFork) {
		Element varDecl = ((VarDecJ2XML)gridVariableDec).getVarElement();
		Element ambitoVar = (Element)varDecl.getParentNode();
		Element forkPosition = ((ForkPointJ2XML)gridifiedFork).getForkElement();		
		NodeList ambitoSentences = ambitoVar.getChildNodes();
		//me muevo hasta la sentencia próxima inmediata al ForkPoint 
		int i = 0;		
		while (i < ambitoSentences.getLength() &&
				forkPosition.compareDocumentPosition(ambitoSentences.item(i)) != Element.DOCUMENT_POSITION_FOLLOWING &&
				forkPosition.compareDocumentPosition(ambitoSentences.item(i)) != 10) //10 significa que forkPosition está contenida en ambitoSentences.item(i) 
			i+=1;
		while (i < ambitoSentences.getLength()){
			NodeList varReferences = this.getVarUses((Element)ambitoSentences.item(i),gridVariableDec.getVarName());
			if (varReferences.getLength()!=0){
				for (int j = 0; j < varReferences.getLength(); j++){
					Node newNode =this.getGetCall(gridVariableDec, gridifiedFork);					
					Element varRef = (Element)varReferences.item(j);
					Node father = varRef.getParentNode(); 
					father.replaceChild(newNode,varRef);
				}
			}			
			i+=1;
		}		
	}
	
	/**{@inheritDoc}<br>
	 * Además de insertar el método getResult, se encarga de insertar la variable HashTable y su inicialización.
	 * Esta tabla se encarga de almacenar los resultados obtenidos por las ejecuciones que se lleven a cabo de forma local.
	 */
	@Override
	protected void insertGetMethod(ForkPoint gridifiedFork){
		MethodDeclaration md = gridifiedFork.getMethodOwner();
		// Inserto la declaración de la Hashtable que guarda los resultados locales al comienzo del método a gridificar
		try {
			Element hash = this.getGetHash(md, "getResultHashInit");
			Element methodSentence = (Element)xpath.evaluate("//"+cm.METHOD+"[@name=\""+md.getMethodName()+"\"]/"+cm.BODYMETHOD, gridifiableClass, XPathConstants.NODE);
			if(!(Boolean) xpath.evaluate("self::*//"+cm.VARREF+"[@name=\"hash\"]",methodSentence,XPathConstants.BOOLEAN)){
				Element hashImport = (Element)gridifiableClass.importNode(hash,true);
				methodSentence.insertBefore(hashImport, methodSentence.getFirstChild());
			}
		} catch (XPathExpressionException e) {e.printStackTrace();}
		// Obtengo e incluyo el método que obtiene el resultado de un llamado Fork (Si ya no se encuentra insertado).
		Element getMethod = this.getGetMethod(md);
		if(getMethod!=null){
			Element nodeToAdd = (Element)gridifiableClass.importNode(getMethod, true);
			Element ambitoClass = (Element)gridifiableClass.getElementsByTagName(cm.CLASS).item(0);
			Element hashTable = (Element)gridifiableClass.importNode(this.getGetHash(md,"getResultHashDeclare"), true);
			ambitoClass.appendChild(nodeToAdd);
			cm.addAttribute(hashTable,ambitoClass.getOwnerDocument());			
		}
	
	}
	
	
	
	/**
	 * Método que realiza las modificaciones necesarias para realizar el control de recursión en el método que se gridificara.
	 * @param fork
	 */
	protected void addRecLevelControl(ForkPoint fork){
		try {
			// Obtengo la declaración del método a gridificar 
			MethodDeclaration md = fork.getMethodOwner();
			Element methodSentence = cm.getMethodImpl(md, gridifiableClass);
			// Inserto al inicio del método el bloque de control del nivel de recursion
			Element methodBody = (Element)((NodeList)methodSentence.getElementsByTagName(cm.BODYMETHOD)).item(0);
			Element recLevelBlock = (Element) xpath.evaluate("//structure[@id='recLevelBlock']/*",preBuildStructures,XPathConstants.NODE);
			recLevelBlock = (Element) gridifiableClass.importNode(recLevelBlock, true);
			methodBody.insertBefore(recLevelBlock, methodBody.getFirstChild());
			
			// modifico los parametros de llamado al método local basandome en los parametros del método a gridificar.
			NodeList allMethodParams = (NodeList)xpath.evaluate(cm.ALLPARAMS+"/"+cm.PARAM, methodSentence, XPathConstants.NODESET);
			Element localMethodCall = (Element)xpath.evaluate("self::*//"+cm.ARGUMENTS,recLevelBlock,XPathConstants.NODE);
			String params = ((Element)allMethodParams.item(0)).getAttribute("name");
			for (int i = 1; i < allMethodParams.getLength(); i++) {
				Element param = (Element)allMethodParams.item(i);
				params += ","+param.getAttribute("name");							
			}
			Element newChild = cm.createNewTag(gridifiableClass, cm.VARREF, params );
			localMethodCall.appendChild(newChild);
			
			//cambiar llamado al metodo local que se llamara <NombreMetodoGrid>_sequential
			Element methodCall = (Element)xpath.evaluate("self::*//"+cm.METHODCALL+"[@message=\"methodNameLocal\"]", recLevelBlock,XPathConstants.NODE);
			methodCall.setAttribute("message",md.getMethodName()+GridGainPeer_J2XML.sequentialMethodSuffix);
			// Agrego el parametro recLevel a la declaración del método gridificado
			Element recLevelParam = (Element)xpath.evaluate("//structure[@id='recLevelParam']/*",preBuildStructures, XPathConstants.NODE);
			Element methodParams = (Element)xpath.evaluate(cm.ALLPARAMS, methodSentence, XPathConstants.NODE);
			recLevelParam = (Element)gridifiableClass.importNode(recLevelParam, true);
			methodParams.appendChild(recLevelParam);
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Método que devuelve un Element una sentencia de la
	 * Hashtable a utilizar para guardar los resultados de los future calculados
	 * de forma local. Las 2 sentencias que se obtienen del documento preBuildStructures
	 * pueden ser getResultHashDeclare o getResultHashInit
	 * @param md declaración del método a gridificar.
	 * @param hashSetence Sentencia que se desea obtener.
	 * @return
	 */	
	private Element getGetHash(MethodDeclaration md,String hashSentence){
		Element hash = null;
		try {
			hash = (Element)xpath.evaluate("//structure[@id=\""+hashSentence+"\"]",preBuildStructures,XPathConstants.NODE);
			hash = (Element)hash.getFirstChild().getNextSibling();
			//cambiar tipo de GR por el adecuado
			NodeList grUses = (NodeList)xpath.evaluate("self::*//"+cm.TYPE+"[@name=\"GRType\"]",hash,XPathConstants.NODESET);
			for (int i = 0; i < grUses.getLength(); i++) {
				((Element)grUses.item(i)).setAttribute("name",GridResult.getGRName(md)); 
			}
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}
		return hash;
	}

	/**
	 * Método encargado de obtener la sentencia de llamada al método que obtiene el resultado de un Fork asignandoselo a
	 * la variable pasada como parametro. Utiliza como base el nodo del preBuildStructure llamado <i>getResultCall</i> 
	 * que posee el formato:<br> varName = this.getResult(params); 
	 * Nota: el Element se asocia al Document donde se encuentra la declaración de variable pasada como parametro
	 * @param varDec variable que recibira el valor obtenido del grid.
	 * @param fork Punto del llamado gridificado. Se utiliza para obtener los parametros de llamado al método gridificado
	 * @return Element que representa el llamado y la asignación con los valores modificados. 
	 */
	@SuppressWarnings("unchecked")
	private Element getGetCall(VarDeclaration varDec,ForkPoint fork) {
		Element genericCall = null;
		try {
		//obtengo el nodo generico a modificar del preBuildStructures
			Element node = (Element)xpath.evaluate("//structure[@id=\"getResultCall\"]/"+cm.METHODCALL,preBuildStructures,XPathConstants.NODE);
			genericCall = (Element) ((VarDecJ2XML) varDec).getVarElement().getOwnerDocument().importNode(node, true);		
		//Cambio el nombre del método a llamar
			genericCall.setAttribute("message", "get"+fork.getMethodName());
		//Agrego los Parametros del llamado al método
			ArrayList<String> paramsName = (ArrayList<String>) ((ForkPointJ2XML)fork).getRealParams().clone();
			if(paramsName.size()>1&&paramsName.get(0).equals("this"))	paramsName.remove(0); //Elimino el llamado al this
			if(paramsName.size()>1&&paramsName.get(0).equals("recLevel"))	paramsName.remove(0); //Elimino el llamado al recLevel
			Element genericParam = (Element)xpath.evaluate("//"+cm.VARREF+"[@name=\"params\"]", genericCall, XPathConstants.NODE);
			//Agrego el parametro del future
			Element futureParam = (Element)genericParam.cloneNode(true); 
			futureParam.setAttribute("name", fork.getGridVariableName());
			genericParam.getParentNode().insertBefore(futureParam, genericParam);
			//Agrego el parametro del nombre de la variable
			Element varParam = (Element)genericParam.cloneNode(true);
			varParam.setAttribute("name","\""+varDec.getVarName()+"\"");
			genericParam.getParentNode().insertBefore(varParam, genericParam);
			for (String param: paramsName) {
				Element newParamCall = (Element)genericParam.cloneNode(true);
				newParamCall.setAttribute("name",param);
				genericParam.getParentNode().insertBefore(newParamCall, genericParam);
			}
			genericParam.getParentNode().removeChild(genericParam);
				
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}
		return genericCall;
	}

	/**
	 * Se encarga de obtener un nuevo método que extrae el resultado de un Fork para 
	 * el MethodDeclaration que recibe como parametro.
	 * @param md Declaración de método para la cual se obtendra el método Get.
	 * @return Element que representa el nuevo método Get.
	 */
	private Element getGetMethod(MethodDeclaration md) {
		Element genericMethod = null;
		try {
			boolean hasGetMethod = (Boolean)xpath.evaluate("//"+cm.METHOD+"/@name='"+"get"+md.getMethodName()+"'", gridifiableClass, XPathConstants.BOOLEAN);
			
			if(!hasGetMethod){//significa que tengo que agregar el método
				genericMethod= (Element)xpath.evaluate("//structure[@id=\"getResultMethod\"]"+"/"+cm.METHOD,preBuildStructures,XPathConstants.NODE);
				// Cambio el nombre del método get por get<NombreMétodoAGridificar>
				genericMethod.setAttribute("name", "get"+md.getMethodName());			
				//cambiar tipo de GR por el adecuado
				NodeList grUses = (NodeList)xpath.evaluate("//"+cm.TYPE+"[@name=\"GRType\"]",genericMethod,XPathConstants.NODESET);
				for (int i = 0; i < grUses.getLength(); i++) {
					((Element)grUses.item(i)).setAttribute("name",GridResult.getGRName(md)); 
				}
				//Modificar el tipo de retorno del método
				Element retNode = (Element)xpath.evaluate(cm.TYPE,genericMethod,XPathConstants.NODE);
				retNode.setAttribute("name", md.getReturnType());
				ArrayList<String> allparamsType = md.getParametersType();
				Element genericParam = (Element)xpath.evaluate("//"+cm.ALLPARAMS+"/"+cm.PARAM+"[@name=\"params\"]", genericMethod, XPathConstants.NODE);
				//agregar parametros a la declaración del metodo
				for (int i = 0; i < allparamsType.size(); i++) {
					String type = allparamsType.get(i);
					Element newParam = (Element)genericParam.cloneNode(true);
					Element paramType = (Element)genericParam.getOwnerDocument().importNode(cm.parseType(type),true);
					newParam.setAttribute("name", "param"+i);
					newParam.removeChild((Element)xpath.evaluate(cm.TYPE,newParam,XPathConstants.NODE));
					newParam.appendChild(paramType);
					genericParam.getParentNode().insertBefore(newParam,genericParam);
				}
				//Elimino el parametro generico de los argumentos
				genericParam.getParentNode().removeChild(genericParam);
				//cambiar llamado al metodo local que se llamara <NombreMetodoGrid>_local
				Element methodCall = (Element)xpath.evaluate("self::*//"+cm.METHODCALL+"[@message=\"methodNameLocal\"]", genericMethod,XPathConstants.NODE);
				methodCall.setAttribute("message",md.getMethodName()+GridGainPeer_J2XML.sequentialMethodSuffix);
				//agregar parametros a la llamada local del metodo
				Element paramsMethodCall = (Element)xpath.evaluate("//"+cm.VARREF+"[@name=\"params\"]", genericMethod, XPathConstants.NODE);
				for (int i = 0; i < allparamsType.size(); i++) {
					Element newParamCall = (Element)paramsMethodCall.cloneNode(true);
					newParamCall.setAttribute("name","param"+i);
					paramsMethodCall.getParentNode().insertBefore(newParamCall, paramsMethodCall);
				}
				// Remuevo el parametro generico
				paramsMethodCall.getParentNode().removeChild(paramsMethodCall);
				// Agrego el parametro de retorno
				Element retStatment = (Element)xpath.evaluate("self::*//literal-number[@kind=\"retType\"]", genericMethod, XPathConstants.NODE);
				retStatment.setAttribute("value", this.getReturnValue(md.getReturnType()));
			}
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}
		return genericMethod;
	}
	
	/**
	 * Dado el método a gridificar, se devuelve el valor de retorno generico del método segun el tipo que retorne
	 * Para valores numericos se retorna 0, para valores boolean se retorna false, para objetos se retorna null y para 
	 * métodos void se retorna un String vacio.
	 * 
	 * @param typeRet tipo de retorno que se desea obtener un valor generico
	 * @return
	 */
	private String getReturnValue(String typeRet) {
		Element typeNode = (Element)cm.parseType(typeRet);
		String type	= typeNode.getAttribute("name");
		String dimension = typeNode.getAttribute("dimensions");		
		if(cm.isPrimitive(type)&& dimension.equals("")){
			if(type.equals("boolean"))
				return "false";
			else
				return "0";
		} else {
			if(type.equals("void"))
				return "";
			else
				return "null";
		}
	}
	
	/** Obtiene todas las ocurrencias de la variable varName contenidas en la sentencia o estructura de
	 * control <B>sentence</B>.
	 * Se tomarán las ocurrencias que se encuentren del lado derecho de una asignación,
	 * las incluidas en parámetros de una llamada a método, las pertenecientes a una condición o a una
	 * sentencia return de un método.
	 * No tiene en cuenta las ocurrencias de la variable del lado izquierdo de una asignación ni
	 * tampoco la declaración de la misma.   
	 * @param sentence
	 * 			representa la sentencia o estructura de control donde se buscarán las ocurrencias de la variable
	 * @param varName
	 * 			el nombre de variable de la que se prentede hallar sus ocurrencias
	 * @return org.w3c.dom.NodeList que contiene nodos que responden a la estructura<BR>
	 * 			&lt var-ref name="varName"&gt */
	private NodeList getVarUses(Element sentence, String varName){
		// expresion xpath: self::*//var-ref[@name='x' and not(count(ancestor::lvalue)>="1")]
		String varNameLocation = "self::*//"+cm.VARREF+"[@name='"+varName+"' and not(count(ancestor::"+cm.ASSIGLEFTVALUE+")>='1')]";
		try {
			return (NodeList)xpath.evaluate(varNameLocation, sentence, XPathConstants.NODESET);
		} catch (XPathExpressionException e) {
			System.err.println("[ERR] Falló recuperación de los usos de la variable"+varName);
			e.printStackTrace();
			return null;
		}
	}	

	/**Dada la declaración original de un método que debe paralelizarse y una clase peer ya construida, retorna la declaración del
	 * método paralelizado asociado al método original recibido como parámetro
	 * @param mdOriginal
	 * 			declaración del método original antes de ser paralelizado
	 * @param peer
	 * 			clase peer paralelizada
	 * @return la declaración del método que se corresponde con la contraparte paralelizada de mdOriginal*/
	public static MethodDeclaration getParallelizedMethodDeclaration(MethodDeclaration mdOriginal, WritebleClass peer) {
		ArrayList<String> parametersType = new ArrayList<String>();
		parametersType.addAll(mdOriginal.getParametersType());
		parametersType.add("int");
		return new MethodDeclaration(peer.getName(), mdOriginal.getReturnType(), mdOriginal.getMethodName(), parametersType);				
	}
	
	
	/**@param parallelizedMethod
	 * 			representa la declaración de un método paralelizado
	 * @param peer
	 * 			representa la clase que contiene el método paralelizado pasado como primer parámetro
	 * @return la declaración del método que se corresponde con la contraparte secuencial del método representado por parallelizedMethod*/
	public static MethodDeclaration getSequencialMethodDeclaration(MethodDeclaration parallelizedMethod, WritebleClass peer){
		ArrayList<String> parametersType = new ArrayList<String>();
		parametersType.addAll(parallelizedMethod.getParametersType());
		parametersType.remove(parametersType.size()-1);
		return new MethodDeclaration(peer.getName(), parallelizedMethod.getReturnType(),
									parallelizedMethod.getMethodName()+GridGainPeer_J2XML.sequentialMethodSuffix, parametersType);
	}	
}
